package jui.business
{
	import com.hurlant.crypto.tls.*;
	import com.seesmic.as3.xmpp.*;
	import com.seesmic.as3.xmpp.xep.publish_subscribe.*;
	
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	
	import jui.model.MessageRenderer;
	
	import mx.collections.ArrayCollection;
	
	
	public class XMPPManager extends EventDispatcher implements IJUIManager
	{
		[Bindable]
		public var messageOutput: String = "";
		
		[Bindable]
		public var messageInput: String = "";
		
		[Bindable]
		public var eventOutput: String = "";

		[Bindable]
		public var rosterData: ArrayCollection = new ArrayCollection();

		[Bindable]
		public var activeRespondentStatusText: String = "";
		
		[Bindable]
		public var activeRespondentJID: String = "juick@juick.com";
		
		private var conn: XMPP;
		
		public var jid: String = "";
		public var pss: String = "";
		public var srv: String = "";

		/**
		 * Returns qualified class name used by ManagersFacrtory to produce this manager.
		 */
		public static function getQName(): String
		{
			return "jui.business.XMPPManager";	
		}
		
		public function connect():void
		{
			trace("Connect with: " + jid + " / " + pss );
			
			//conn = new XMPP(jidInput.text,passInput.text,serverInput.text); //<-- this still works as well.
			// setup the class
			conn = new XMPP();
			//you could do these as separate conn. lines
			
			conn.setJID(jid).setPassword(pss).setServer(srv);

			//Set up the listeners for all of the event types you care about
			conn.addEventListener(XMPPEvent.MESSAGE, handleMessage);
			conn.addEventListener(XMPPEvent.SESSION, handleSession);
			conn.addEventListener(XMPPEvent.SECURE, handleSecure);
			conn.addEventListener(XMPPEvent.AUTH_SUCCEEDED, handleAuthSucceed);
			conn.addEventListener(XMPPEvent.AUTH_FAILED, handleAuthFailed);
			conn.addEventListener(XMPPEvent.PRESENCE_AVAILABLE, handlePresence);
			conn.addEventListener(XMPPEvent.PRESENCE_UNAVAILABLE, handlePresenceUnAvail);
			conn.addEventListener(XMPPEvent.PRESENCE_ERROR, handlePresenceError);
			conn.addEventListener(XMPPEvent.PRESENCE_SUBSCRIBE, handlePresenceSubscribe);
			conn.addEventListener(XMPPEvent.ROSTER_ITEM, handleRoster);
			conn.socket.addEventListener(StreamEvent.DISCONNECTED, handleDisconnected);
			conn.socket.addEventListener(StreamEvent.CONNECT_FAILED, handleConnectFailed);
			conn.socket.addEventListener(StreamEvent.CONNECTED, handleConnected);
			
			// you can also load plugins for non-core XMPP features (see http://xmpp.org/extensions)
			conn.addPlugin(new PublishSubscribe());
			//set up your plugin's events
			conn.plugin['pubsub'].addEventListener(PubSubEvent.ITEM, handlePubSubItem);
			conn.plugin['pubsub'].addEventListener(PubSubEvent.RETRACT, handlePubSubRetract);
			
			
			
			rosterData.removeAll();
			
			//We have to manually pass in the hurlant classes so as to keep them from being a dependency
			//make sure that you did import com.hurlant.crypto.tls.*;
			conn.setupTLS(TLSEvent, TLSConfig, TLSEngine, TLSSocket);
			
			//Once you connect(), events should start flowing.
			conn.connect();
		}		

		public function setRespondentUser(jUserVO: UserVO): void
		{
			activeRespondentJID = jUserVO.jid; 
			
			var status: Dictionary = conn.roster.getHighestStatus(activeRespondentJID);
			activeRespondentStatusText = jUserVO.name + ' [' + status['type'] + '] ' + status['status'] + "\n"; 
		}
		
		public function connectAsUser(jUserVO: UserVO): void
		{
			jid = jUserVO.jid;
			pss = jUserVO.pass;
			srv = jUserVO.srv;
			
			connect(); 
		}
		
		public function disconnect():void
		{
			if(conn != null)
			{
				conn.disconnect();
			}
		}
		
		public function sendMessageTo(toJID: String, messageText: String): void
		{
			var targetJid: String = toJID || activeRespondentJID;
			conn.sendMessage( targetJid, messageText );
			trace("Send: " + targetJid + ", " + messageText); 
			// DISABLED appendMessageOutput( xmppManager.conn.fulljid.toString() + ': ' + messageInput.text + '\n' );			
		}
		
		/**
		 * Send invalid XML, useful for testing auto-reconnect from stream error
		 */
		public function breakIt():void
		{
			conn.send("<howdy></hi>");
		}
		
		private function appendMessageOutput(output:String):void 
		{
			//messageOutput.styleSheet = styleSheet;
			
			var newMessage: String =
				'<p class="outputMessageWrapper">' + output + '</p>' +
				'<p class="separatorOutputMessage">----------------------------------------------------</p>';
			
			messageOutput += newMessage;
			
			trace("MESAGE OUTPUT:\n" + messageOutput);
		}		

		private function handlePubSubItem(e:PubSubEvent): void 
		{
			//You'd probably actually do something with the pubsub event here
			eventOutput += "Received PubSub Item\n";
		}
		
		private function handlePubSubRetract(e:PubSubEvent):void {
			eventOutput += "Received PubSub Retract\n";
		}
		
		private function handleMessage(e:XMPPEvent): void 
		{
			//the XMPPEvent contains .stanza, which is the stanza instance of whatever caused the event
			if(!e.stanza.body) return;
			var m: MessageRenderer = new MessageRenderer();
			m.body = e.stanza.body;
			
			if(e.stanza.xml)
			{
				m.xml = e.stanza.xml;
			}
			
			appendMessageOutput(m.toHTML());
			
			/* TODO */
			// DISABLED: auto-swithces user to last message received from
			//activeRespondentJID = e.stanza.from;
			//if(e.stanza.hasPlugin('twitter')) {
				// documentation on this to come
			//	eventOutput += e.stanza.p.twitter.link + "\n";
			//}
		}
		
		private function handlePresence(e:XMPPEvent):void {
			appendMessageOutput( e.stanza.from + '[' + e.stanza.type + '] ' + e.stanza.status + "\n" );
			eventOutput += e.stanza.from.getBareJID() + " is online.\n"; 
		}
		
		private function handlePresenceUnAvail(e:XMPPEvent):void {
			eventOutput += e.stanza.from.getBareJID() + " is offline.\n"; 
		}
		
		private function handlePresenceError(e:XMPPEvent):void {
			eventOutput += e.stanza.from.getBareJID() + " presence error event.\n"; 
		}
		
		private function handlePresenceSubscribe(e:XMPPEvent):void {
			eventOutput += e.stanza.from.getBareJID() + " presence subscription event.\n"; 
		}
		
		private function handleRoster(e:XMPPEvent):void {
			var name:String = e.stanza['nick'];
			if(!name) name = e.stanza['jid'];
			var item:Dictionary = conn.roster.items[e.stanza.jid];
			var found:Boolean = false;
			
			// Populate roster data provider with data
			for(var index:String in rosterData) {
				if(e.stanza.jid == rosterData[index]['jid']) {
					rosterData[index] = item;
					found = true;
					break;
				}
			}
			if(!found) rosterData.addItem(item);
		}
		
		private function handleSession(e:XMPPEvent):void {
			//you pretty much always want to do this
			eventOutput += "Session Started\n";
			conn.getRoster();
			conn.sendPresence();
		}
		
		private function handleSecure(e:XMPPEvent):void {
			eventOutput += "Encryption Enabled\n";
		}
		
		private function handleAuthSucceed(e:XMPPEvent):void {
			eventOutput += "Authorized\n";
		}		

		private function handleAuthFailed(e:XMPPEvent):void {
			eventOutput += "Authorization Failed\n";
		}
		
		private function handleDisconnected(e:StreamEvent):void {
			eventOutput += "Disconnected\n";
		}
		
		private function handleConnected(e:StreamEvent):void {
			eventOutput += "Connected\n"; 
		}
		
		private function handleConnectFailed(e:StreamEvent):void {
			eventOutput += "Failed to connect\n";
		}
		
		public function XMPPManager(target:IEventDispatcher=null)
 		{
 			super(target);
 		}
	}
}